#include "stdafx.h"

static const int lineWidth = 8;
//static const double min_pipe_length = 10;


Pipe::Pipe(const QPointF &_source, const QPointF &_dest)
	: source(_source), dest(_dest)
{
	movable = false;
	//	setFlag(ItemIsMovable);
	setFlag(ItemIsSelectable);
	setFlag(ItemSendsGeometryChanges);
	setCacheMode(DeviceCoordinateCache);
	setZValue(-1);
	this->setPen(QPen(Qt::black, lineWidth + 6));

}

Pipe::~Pipe(){}

QRectF Pipe::boundingRect() const{
	QRectF rect = QRectF(source, QSizeF(dest.x() - source.x(), dest.y() - source.y()))
		.normalized()
		.adjusted(-lineWidth, -lineWidth, lineWidth, lineWidth);
	return rect;
}

void Pipe::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *){
	painter->setRenderHint(QPainter::Antialiasing);
	QLineF line(source, dest);
	if (isSelected()){
		painter->setPen(QPen(QColor(0, 162, 232, 100), lineWidth + 10, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));	
		painter->drawLine(line);
	}

	static const double Pi = 3.14;
	static const double twoPi = 2.0 * Pi;
	static const int arrowSize = 7;
	QColor color;

	color.setRgb(125, 125, 140);
	painter->setPen(QPen(color, lineWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
	painter->drawLine(line);

	double angle = ::acos(line.dx() / line.length());
	if (line.dy() >= 0)
		angle = twoPi - angle;
	QPointF arrowP1 = QPointF(sin(angle - Pi / 3) * arrowSize,
		cos(angle - Pi / 3) * arrowSize);
	QPointF arrowP2 = QPointF(sin(angle - Pi + Pi / 3) * arrowSize,
		cos(angle - Pi + Pi / 3) * arrowSize);

	color.setRgb(85, 85, 95);
	painter->setPen(QPen(color, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
	color.setRgb(200, 200, 220);
	painter->setBrush(QBrush(color, Qt::SolidPattern));
	QPointF arrowPM;
	for (int i = 15; i < line.length() - 15; i += 100){
		arrowPM = source - (source - dest)*i/line.length();
		painter->drawPolygon(QPolygonF() << arrowPM << arrowP1 + arrowPM << arrowP2 + arrowPM);
	}

	this->setLine(line);

}

void Pipe::mousePressEvent(QGraphicsSceneMouseEvent *event){
	movable = true;
	update();
	QGraphicsItem::mousePressEvent(event);

}

void Pipe::mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
	movable = false;
	update();
	QGraphicsItem::mouseReleaseEvent(event);
}

void Pipe::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
	if (movable){
		if (orientation() == VERTICAL){
			source.setX(event->scenePos().x());
			dest.setX(event->scenePos().x());
		}
		if (orientation() == HORIZONTAL){
			source.setY(event->scenePos().y());
			dest.setY(event->scenePos().y());
		}
		emit positionChanged(this);
	}
	update();
	QGraphicsItem::mouseMoveEvent(event);
}

QVariant Pipe::itemChange(GraphicsItemChange change, const QVariant &value){
	if(change == ItemPositionHasChanged){

	}
	if(change == ItemSelectedHasChanged){
		emit selectionChanged(isSelected());
	}
	return QGraphicsItem::itemChange(change, value);
}

Pipe::pipe_orientation_t Pipe::orientation(){
	pipe_orientation_t orientation = NONE;
	if (source.x() == dest.x())	orientation = VERTICAL;
	if (source.y() == dest.y()){
		if(orientation == VERTICAL) orientation = ZERO;
		else orientation = HORIZONTAL;
	}
	return orientation;
}


//=====================================================================================================================
//=====================================================================================================================
//=====================================================================================================================

PipeLine::PipeLine(SceneView *_scene, Node *_source, Node *_dest, QString pipesCoordinates, QString _name)
	: scene(_scene), source(_source), dest(_dest)
{
	if (_name == "") _name = getDefaultName();
	title = new Title(this, _name, QPointF());
	title->adjust();
	init_model();
	add_pipes(pipesCoordinates);
	scene->addItem(title);
	QPointF title_pos = (pipes[int(pipes.size()/2)]->source + pipes[int(pipes.size()/2)]->dest) / 2;
	title->setPos(title_pos);
	pipes.front()->setSelected(false);
}

PipeLine::~PipeLine(){
	scene->pipeline_list.erase(find(scene->pipeline_list.begin(), scene->pipeline_list.end(), this));
	source->removeInletPipe(this);
	dest->removeOutletPipe(this);
	foreach(Pipe *p, pipes) delete p;
	delete title;
	delete model;
}

QString PipeLine::getDefaultName(){
	QString text;
	int num = 0;
	bool findNum = true;
	while (findNum){
		findNum = false;
		num++;
		text = "path " + QString::number(num);
		foreach (PipeLine *pipeline, scene->pipeline_list)
			if (pipeline->title->title() == text){
				findNum = true;
				break;
			}
	}	
	return text;
}

void PipeLine::add_pipes(const QString &coordinates){
	if(coordinates != ""){
		QStringList coordinate_list = coordinates.split(";");
		foreach(QString line, coordinate_list){
			double x1, x2, y1, y2;
			QString x = line.split(":").front();
			QString y = line.split(":").back();
			x1 = x.split("-").front().toDouble();
			x2 = x.split("-").back().toDouble();
			y1 = y.split("-").front().toDouble();
			y2 = y.split("-").back().toDouble();
			Pipe *pipe = addPipe(pipes.size(), QPointF(x1, y1), QPointF(x2, y2));
		}
	}else{
		if(source->pos().y() != dest->pos().y())
			Pipe *pipe = addPipe(pipes.size(), source->pos(), QPointF(source->pos().x(), dest->pos().y()));
		if(source->pos().x() != dest->pos().x())
			Pipe *pipe = addPipe(pipes.size(), QPointF(source->pos().x(), dest->pos().y()), dest->pos());
	}
}


QString PipeLine::get_pipes_coordinate(){
	QString coordinate = "";
	foreach(Pipe *pipe, pipes){
		if (coordinate != "") coordinate += ";";
		if(pipe->orientation() == Pipe::HORIZONTAL)
			coordinate += QString::number(pipe->source.x()) + "-" + QString::number(pipe->dest.x()) + ":" + QString::number(pipe->source.y());
		else
			coordinate += QString::number(pipe->source.x()) + ":" + QString::number(pipe->source.y()) + "-" + QString::number(pipe->dest.y());
	}
	return coordinate;
}


void PipeLine::adjust(){
	if(source->pos() != pipes.front()->source)
		resizeNext(0, source->pos());
	if(dest->pos() != pipes.back()->dest)
		resizePrevious(pipes.size() - 1, dest->pos());
}

void PipeLine::resizeNext(int index, const QPointF &new_point){
	Pipe *pipe = pipes[index];
	Pipe::pipe_orientation_t orientation = pipe->orientation();
	pipe->source = new_point;
	if(orientation == Pipe::HORIZONTAL){
		pipe->dest.setY(new_point.y());
		if(pipe != pipes.back()){
			if(pipes[index + 1]->source != pipe->dest){
				resizeNext(index + 1, pipe->dest);
			}
		}else{
			if(pipe->dest != dest->pos()){
				addPipe(pipes.size(), pipe->dest, QPointF(pipe->dest.x(), dest->pos().y()));//////
			}
		}
	}
	if(orientation == Pipe::VERTICAL){
		pipe->dest.setX(new_point.x());
		if(pipe != pipes.back()){
			if(pipes[index + 1]->source != pipe->dest){
				resizeNext(index + 1, pipe->dest);
			}
		}else{
			if(pipe->dest != dest->pos()){
				addPipe(pipes.size(), pipe->dest, QPointF(dest->pos().x(), pipe->dest.y()));
			}
		}
	}
	if(orientation == Pipe::ZERO){
		checkOrientation(index);
		deletePipe(index);
	}
}

void PipeLine::resizePrevious(int index, const QPointF &new_point){
	Pipe *pipe = pipes[index];
	Pipe::pipe_orientation_t orientation = pipe->orientation();
	pipe->dest = new_point;
	if(orientation == Pipe::HORIZONTAL){
		pipe->source.setY(new_point.y());
		if(pipe != pipes.front()){
			if(pipes[index - 1]->dest != pipe->source){
				resizePrevious(index - 1, pipe->source);
			}
		}else{
			if(pipe->source != source->pos()){
				addPipe(0, QPointF(pipe->source.x(), source->pos().y()), pipe->source);
			}
		}
	}
	if(orientation == Pipe::VERTICAL){
		pipe->source.setX(new_point.x());
		if(pipe != pipes.front()){
			if(pipes[index - 1]->dest != pipe->source){
				resizePrevious(index - 1, pipe->source);
			}
		}else{
			if(pipe->source != source->pos()){
				addPipe(0,  QPointF(source->pos().x(), pipe->source.y()), pipe->source);
			}
		}
	}
	if(orientation == Pipe::ZERO){
		checkOrientation(index);
		deletePipe(index);
	}
	
}

void PipeLine::checkOrientation(int index){
	if (index > 0 && index < pipes.size() - 1){
		if(pipes[index - 1]->orientation() == pipes[index + 1]->orientation()){
			if(pipes[index - 1]->orientation() == Pipe::HORIZONTAL)
				pipes[index - 1]->dest.setX(pipes[index + 1]->dest.x());
			if(pipes[index - 1]->orientation() == Pipe::VERTICAL)
				pipes[index - 1]->dest.setY(pipes[index + 1]->dest.y());
			
			if (index + 2 < pipes.size())
				resizeNext(index + 2, pipes[index - 1]->dest);
			else
				if(pipes.back()->dest != dest->pos())
					addPipe(pipes.size(), pipes[index - 1]->dest, dest->pos());
			deletePipe(index + 1);
		}
	}
}

Pipe* PipeLine::addPipe(int index, const QPointF &_source, const QPointF &_dest){
	Pipe *pipe = new Pipe(_source, _dest);
	pipes.insert(pipes.begin() + index, pipe);
	scene->addItem(pipe);
	connect(pipe, SIGNAL(positionChanged(Pipe *)), this, SLOT(userChangePosition(Pipe *)));
	connect(pipe, SIGNAL(selectionChanged(bool)), this, SLOT(userChangeSelection(bool)));
	userChangeSelection(pipes[0]);
	return pipe;
}

void PipeLine::deletePipe(int index){
	Pipe *pipe = pipes[index];
	scene->removeItem(pipe);
	vector<Pipe*>::iterator it = find(pipes.begin(), pipes.end(), pipe);
	pipes.erase(it);
	//delete pipe;
}

void PipeLine::userChangePosition(Pipe *pipe){
	for (int i = 0; i < pipes.size(); i++){
		if (pipes[i] == pipe){
			if (i + 1 < pipes.size())	resizeNext(i + 1, pipes[i]->dest);
			else	if (pipe->dest != dest->pos())	addPipe(pipes.size(), pipes.back()->dest, dest->pos());

			if (i - 1 >= 0)	resizePrevious(i - 1, pipes[i]->source);
			else	if (pipe->source != source->pos())	addPipe(0, source->pos(), pipes.front()->source);
			break;
		}
	}
}

void PipeLine::userChangeSelection(bool selected){
	foreach(Pipe *pipe, pipes)
		pipe->setSelected(selected);
	scene->main->setPropertiesModel(model);
}

void PipeLine::init_model(){
	//0 - boolean
	//1 - spinbox
	//2 - textbox
	//3 - label
	//4 - combobox
	//5 - button
	model = new PropertiesModel(this);
	model->addItem("NAME", title->title(), "", 2, 0,"name must be unique.");
	model->addItem("DISPLAY TITLE", title->isVisible(), "", 0, 0, "Use to show/hide title");
	model->addItem("PROFILE", "piecewise-constant/coordinate,height/km,m/", "edit", 5, 0, "profile...");
	model->addItem("MAXSL", 0, "m", 1, 0, "maximum segment length");
	model->addItem("DIAMETER", 0, "m", 1, 0, "");
	model->addItem("ABS ROUGHNESS", 0, "m", 1, 0, "");
    model->addItem("WALL THICKNESS", 0, "m", 1, 0, "");


	connect(this, SIGNAL(setModelData(const int &, const int &, const QVariant &)), 
		model, SLOT(changeData(const int &, const int &, const QVariant &)));

	connect(model, SIGNAL(ItemChanged(const int &, const int &, const QVariant &)),
		this, SLOT(getModelData(const int &, const int &, const QVariant &)));

	connect(model, SIGNAL(update()), this, SLOT(updateProperties()));
}


void PipeLine::getModelData(const int &parent_row, const int &row, const QVariant &value){
	if (parent_row == -1 && row == 0){
		title->setTitle(value.toString());
		return;
	}
}

void PipeLine::updateProperties(){
	scene->main->properties->setItemDelegate(new Delegate(scene->main));
}

bool PipeLine::isSelected(){
	return pipes[0]->isSelected();
}